perm filename REVSND[REV,MUS] blob sn#265524 filedate 1977-05-24 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00005 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00002 00002	How to reverberate a sound file:
C00008 00003		How to calculate Norms and Permute
C00010 00004	How to create TREE of states:
C00014 00005	How to fetch units from REV file:
C00016 ENDMK
C⊗;
How to reverberate a sound file:

In what follows, all tree traversals are prefix walks, i.e. the order is:
	(process this node,
	traverse the OUTPUT subtree of this node,
	traverse all the ABURO subtrees of this node).
A node in the tree is of the form:
	[OUTPUT: pointer to first output node,
	 ABURO: pointer to younger sibs of this node,
	 fields for the parameters to APR for this unit]
Each node in the tree contains a description of a single unit reverberator
sufficient for calling the all-pass reverberator simulator routine APR.  The
best version of that routine is to be found on my area [REV,KS] named APRL.FAI,
where the L means lattice form.  The source includes a sample SAIL declaration.
Various programming notations used below are explained afterward.  For simplicity,
I will use, e.g. ABURO[node], to mean TREE:ABURO[node] if no ambiguity results.

_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-

Create TREE, counting  POLY ← # of branches.

Allocate array of output buffers  REAL Bufs[1:POLY,1:BUFSIZE];
Allocate parallel array of normalizing factors  REAL Norms[1:POLY];
Allocate parallel array of  PNT(TREE) Backup[0:POLY];
Allocate parallel array of indices  INT Permute[0:POLY];
For efficiency, these can be made static (OWN) by assuming that
	POLY never exceeds 4 (quad reverberator).  POLY must still
	be determined however.
Calculate Norms and Permute; {Explained below.}

Init input file, output file;


LOOP:	this_one ← 1; next_one ← 2; Backup[*] ← λ; node ← first node in tree;
   Read input into Bufs[Permute[this_one],*];
WHILE ¬eof: {reverberate this buffer into POLY channel buffers}
   LOOP:WHILE node ≠ λ: {go thru TREE}
      IF
         ABURO[node] = λ
       THEN
         gets ← this_one
       ELSE
         gets ← next_one; next_one ← next_one+1;
         Backup[this_one] ← ABURO[node];
      APR(Bufs[Permute[this_one],1],Bufs[Permute[gets],1],BUFSIZE,
          ..unit parameters..);
      this_one ← gets;
      Backup[this_one] ← OUTPUT[node];
      LOOP:WHILE Backup[this_one] = λ  ∧  this_one > 0:
         this_one ← this_one-1;
      REPEAT;
      node ← Backup[this_one];
   REPEAT;

   Multiply each buffer by the appropriate normalizing factor:
      ∀(1 ≤ i ≤ POLY,1 ≤ j ≤ BUFSIZE) Bufs[i,j] ← Bufs[i,j]*Norms[i];

   {now multiplex buffers into output. This is clever.}
   {treat Bufs as 1-dimensional array, indexed from 0}
   i ← 0;
   LOOP: {get the next output sample}
      next output sample ← Bufs[i];
      i ← i+BUFSIZE;
   WHILE i ≠ (POLY+1)*BUFSIZE:
      IF
         i ≥ POLY*BUFSIZE
       THEN
         i ← i+1-POLY*BUFSIZE;
   REPEAT;
   Write output;
REPEAT;

_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-

The looping syntax used is from Knuth's "Structured programming with goto's",
and is interpretted as follows:
	LOOP: S1; WHILE B: S2; REPEAT;
means execute S1, then test condition B; if B is FALSE, then exit the loop, else
execute S2 and repeat the loop starting at S1 again.
Where I refer to array subscripts with the notation A[*] or B[1,*] I mean all
values of that particular index; so that A[*]←λ means
FOR i←lower_bound_of_A STEP 1 UNTIL upper_bound_of_A DO A[i]←λ;
I use the abbreviation λ to mean NULL_RECORD.
Curly brackets {} surround comments.
Specific details of I/O are left as an exercise for the reader.
	How to calculate Norms and Permute

PROCEDURE normute(
      RECORD_POINTER(TREE) node;
      REAL gain;
      REFERENCE REAL ARRAY Norms;
      REFERENCE INTEGER ARRAY Permute;
      REFERENCE INTEGER this, next, out#);
   BEGIN "normute"
   REAL lgain;
   DO BEGIN
      lgain ← gain * GAIN[node];
      IF
         ABURO[node] ≠ λ
       THEN BEGIN
         this ← next;
         next ← next+1;
         END;
      IF
         OUTPUT[node] = λ
       THEN BEGIN
         Permute[this] ← out#;
         Norms[out#] ← 1.0/lgain;
         out# ← out#+1;
         DO
            this ← this-1
          UNTIL
            Permute[this] = 0;
         END
       ELSE
         normute(OUTPUT[node],lgain,Norms,Permute,this,next,out#);
      node ← ABURO[node];
      END
    UNTIL
      node = λ;
   END   "normute"

_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-

Call this routine with:
	normute(root of tree,1.0,Norms,Permute,tmp1,tmp2,tmp3);
How to create TREE of states:

1) write an input routine 
	"BOOL PROC fetch_unit(INT channel; REF INT pos, delay; REF REAL gain)"
which returns TRUE iff it could fetch another unit, and FALSE with pos=INFINITY
otherwise. Pos = -1 => Append, 0 => Branch, >0 => pop(Pos times)&Branch.
It can tell when there are no more units by encountering a blank line.

2) write a routine
	"PNT(TREE) PROC grow(INT delay; REAL gain)"
which constructs a node of form:
	TREE[OUTPUT: pointer to first output node,
	     ABURO: pointer to first younger sib node,
		the following are parameters to the APR routine ...
	     MEM[1:delay]: array for delay memory,
	     delay: length of delay memory,
	     pos: pointer into MEM maintained by APR,
	     gain: attenuation factor for this unit]

3) use the recursive sub-tree builder listed below
	"PROC acorn(INT channel; PNT(TREE) node;
			REF INT poly, pos, delay; REF REAL gain)"
which uses (1) and (2) to build the TREE.

_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-

PROCEDURE acorn(
      INTEGER channel;
      RECORD_POINTER(TREE) node;
      REFERENCE INTEGER poly, pos, delay;
      REFERENCE REAL gain);
   BEGIN "acorn"
   WHILE
      fetch_unit(channel,delay,pos,gain)
    DO BEGIN
      IF
         pos < 0
       THEN BEGIN
         OUTPUT[node] ← grow(delay,gain);
         acorn(channel,OUTPUT[node],poly,pos,delay,gain);
         END;
      IF
         pos > 0
       THEN BEGIN
         pos ← pos-1;
         RETURN;
         END;
      ABURO[node] ← grow(delay,gain);
      node ← ABURO[node];
      poly ← poly+1;
      END;
   pos ← INFINITY;
   END   "acorn";

_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-

Call acorn as follows:
	ignore commands from REV file thru first blank line.
	next line is Clock rate; input sound file must use same rate.
	next line may be ignored.
	next line is the first line to be read by fetch_unit.
	call acorn(channel,rev←NEW_RECORD(TREE),poly←1,tmp,tmp,tmp).
	then throw away first node in tree.
How to fetch units from REV file:

Units will be described by two lines like this:
#SAMPLES 9999
GAIN .999 DELAY
The fetch_unit routine should simply return these two values in the corresponding
reference parameters delay(i.e. #samples delay) and gain.

These two lines will be immediately followed (no intervening blank lines) by
some number of lines describing where to put this unit in the tree constructed
so far.  There are two possibilities:
APPEND
or
{←}*
BRANCH
The second case means there will be 0 or more lines containing the single
command "←", followed by a line with the BRANCH command.  In this case, pos
should be the number of "←" commands seen.  In the APPEND case, pos should
be returned as -1.

If a blank line is seen, then there are no further units in the tree, so the
boolean value of the routine should be FALSE.  In this case, pos should be
returned with the value INFINITY.  If a unit is found (the usual case), then
the value of the routine should be TRUE to indicate success.